package cn.zhibang.yygh2.Server.gateway.filter;

import cn.zhibang.yygh2.common.util.result.Result;
import cn.zhibang.yygh2.common.util.result.ResultCodeEnum;
import cn.zhibang.yygh2.common.util.util.JwtHelper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 全局Filter过滤器，统一处理会员登录与外部不允许访问的服务
 * 功能1：统一处理会员登录
 * 功能2：过滤外部不允许访问的服务
 * 业务：用户发起Get、Post请求，通过网关gateway的GlobalFilter进行拦截—>获取参数—>处理并包装—>转到下一级
 * GlobalFilter：全局过滤器接口
 *      重写filter()过滤方法，编号过滤规则。
 * Ordered：排序接口
 *      为什么要排序接口？
 *      我们知道spring框架使用了大量的策略设计模式。策略设计模式意味着我们的同一个接口，会有大量的不同实现。那么这么多实现，先执行哪个，后执行哪个呢。这就产生了一个排序和优先级的问题，于是Ordered接口登场，用来解决这一问题。
 *      重写getOrder()方法
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    //定义ant风格的路径匹配器
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获得request请求对象
        ServerHttpRequest request = exchange.getRequest();
        //2.获得请求url
        String path = request.getURI().getPath();

        //内部服务接口，不允许外部访问
        //如果当前请求path中包含有满足/**/inner/**规则的路径
        //即；是登录URL才做过滤，不是登录URL没必要做过滤
        if(antPathMatcher.match("/**/inner/**", path)) {
            //通过交换器响应
            ServerHttpResponse response = exchange.getResponse();
            //调用out()方法，响应“没有权限”
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //TODO：调用getUserId()方法，获取当前用户id
        Long userId = this.getUserId(request);
        //api接口，异步请求，校验用户必须登录
        if(antPathMatcher.match("/api/**/auth/**", path)) {
            //判断用户id字符串是否为空
            if(StringUtils.isEmpty(userId)) {
                //通过交换器响应
                ServerHttpResponse response = exchange.getResponse();
                //不予访问
                //调用out()方法，响应“未登录”
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }
        return chain.filter(exchange);
    }

    /**
     * Ordered接口类方法
     * 作用：获得当前实现类的order数值
     *      最大值2147483647到最小值-2147483648，值越小优先级越高
     */
    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * api接口鉴权失败返回数据
     * @param response
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //创建一个result对象，并传入错误信息LOGIN_AUTH(208, "未登陆"),
        Result result = Result.build(null, resultCodeEnum);
        //通过fastjson转换成byte[]
        byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        //指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 获取当前登录用户id
     * @param request
     * @return
     */
    private Long getUserId(ServerHttpRequest request) {
        //声明token
        String token = "";
        //获取请求头中的token
        List<String> tokenList = request.getHeaders().get("token");
        //判断装token的集合不为空
        if(null != tokenList) {
            //将请求头中获取的token赋给声明的token
            token = tokenList.get(0);
        }
        //判断token是否为空
        if(!StringUtils.isEmpty(token)) {
            //获得用户id
            return JwtHelper.getUserId(token);
        }
        return null;
    }
}

